package metier;

import client.*;

import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
import java.util.*;
import java.io.*;
import sun.audio.*;
import java.util.Arrays;


public class Simulateur extends JFrame implements MouseListener, MouseMotionListener, KeyListener {

    //Attributs
    private Simulateur.MyPanel panel;
    private int tailleGrille;
    public int caseAPlacer = 1;
    private int simMode = 0;
    private int boutonSouris = 0;
    private SimIHM ihm;
    
    //Constructeur
    public Simulateur(SimIHM ihm,int taille) {
        super("SimForest");
        this.ihm = ihm;
        tailleGrille = taille;
        panel = new Simulateur.MyPanel(taille);
        this.add(panel);
        panel.addMouseMotionListener(this);
        panel.addMouseListener(this);
        panel.addKeyListener(this);
        
        setSize(taille * 5 + 7, taille * 5 + 29);
        setVisible(true);
        setResizable(false);
        setLocation(Toolkit.getDefaultToolkit().getScreenSize().width/2-getWidth()/2,
                Toolkit.getDefaultToolkit().getScreenSize().height/2-getHeight()/2);
        panel.setFocusable(true);
        panel.requestFocus();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    
    //Methodes
    public void effacerFeu() {
        for(int X=0; X<tailleGrille; X++){
            for(int Y=0; Y<tailleGrille; Y++){
                if (panel.grille[X][Y] == 5 || panel.grille[X][Y] == 6) {
                    panel.grille[X][Y] = 0;
                }
            }
        }
        panel.repaint();
    }
    
    public String requeteSauvegarde(int id){
        String query = "";
        for (int X=0; X<tailleGrille; X++){
            for (int Y=0; Y<tailleGrille; Y++){
                if (panel.grille[X][Y]==0) {continue;}
                query += "INSERT INTO CASES (ID, TYPE, X, Y) VALUES ("
                        +id+", "+panel.grille[X][Y]+","+X+", "+Y+"); ";
            }
        }
        
        //System.out.println(query);
        return query;
    }
    
    public void ecrireCsv(String nom){
        int A = 0, a = 0, p = 0, v = 0, nc = tailleGrille * tailleGrille;
        float dA, da, dp, dv;
        for (int X=1; X<tailleGrille-1; X++){
            for (int Y=1; Y<tailleGrille-1; Y++){
                if (panel.grille[X][Y]==0) {v++;}
                if (panel.grille[X][Y]==1) {p++;}
                if (panel.grille[X][Y]==2 || panel.grille[X][Y]==3) {a++;}
                if (panel.grille[X][Y]==4) {A++;}
            }
        }
        dv = (float)(v)/(float)nc;
        dp = (float)(p)/(float)nc;
        da = (float)(a)/(float)nc;
        dA = (float)(A)/(float)nc;
        
        try {
            FileWriter writer = new FileWriter("csv/"+nom+".csv", true);
            
            BufferedReader br = new BufferedReader(new FileReader("csv/"+nom+".csv"));     
            if (br.readLine() == null) {
                writer.append("jeune pousse;arbuste;arbre;vide\n");
            }
            
            writer.append(dp+";"+da+";"+dA+";"+dv+"\n");
            
            writer.flush();
            writer.close();
        } catch (IOException exc){
            System.out.println("Erreur d'ecriture : "+exc.toString());
        }
    }
    
    public void remplacerGrille(int[][] newGrille){
        for (int X=0; X<newGrille.length; X++){
            for (int Y=0; Y<newGrille.length; Y++){
                if (newGrille[X][Y]!=0) {
                    panel.grille[X][Y] = newGrille[X][Y];
                }
            }
        }
        panel.repaint();
    }
    
    public void effacerInsectes() {
        for(int X=0; X<tailleGrille; X++){
            for(int Y=0; Y<tailleGrille; Y++){
                if (panel.grille[X][Y] == 7) {
                    panel.grille[X][Y] = 0;
                }
            }
        }
        panel.repaint();
    }
    
    public void effacerGrille() {
        for(int X=0; X<tailleGrille; X++){
            for(int Y=0; Y<tailleGrille; Y++){
                panel.grille[X][Y] = 0;
            }
        }
        panel.repaint();
    }
    
    public void simulerRafraichir(int nombrePas){
        panel.simuler(nombrePas);
        panel.repaint();
    }
    
    public void autoSimuler(float secondes){
        int millisecondes = (int)(secondes*1000);
        panel.lancerTimer(millisecondes);
    }
    
    public void stoperAuto(){
        panel.stoperTimer();
    }
    
    public void setCaseAPlacer(int n){
        caseAPlacer = n;
    }
    
    public void setSimMode(int n){
        simMode = n;
        switch (n){
            case 0:
                effacerFeu();
                effacerInsectes();
                caseAPlacer = 1;
            break;
                
            case 1:
                effacerInsectes();
                caseAPlacer = 5;
            break;
                
            case 2:
                effacerFeu();
                caseAPlacer = 7;
            break;
        }
    }
    
    private void clicSouris(MouseEvent e){
                        //System.out.println("mx : "+e.getX()+", my : "+e.getY()+", clic : "+e.getButton());
        if (boutonSouris==1) {
            int mx = (e.getX()-e.getX()%5)/5;
            int my = (e.getY()-e.getY()%5)/5;
            if (mx<tailleGrille-1 && my<tailleGrille-1
                    && mx>0 && my>0) {
                panel.grille[mx][my] = caseAPlacer;
                repaint();
            }
        }
        
        if (boutonSouris==3) {
            int mx = (e.getX()-e.getX()%5)/5;
            int my = (e.getY()-e.getY()%5)/5;
            if (mx<tailleGrille-1 && my<tailleGrille-1) {
                panel.grille[mx][my] = 0;
                repaint();
            }
        }
    }
    
    public int getSimMode(){
        return simMode;
    }
    
    public int getTailleGrille(){
        return panel.tailleGrille;
    }
    //Evenements souris
    @Override
    public void mouseClicked(MouseEvent e){}
    @Override
    public void mousePressed(MouseEvent e){
        boutonSouris = e.getButton();
        clicSouris(e);
    }
    @Override
    public void mouseReleased(MouseEvent e){}
    @Override
    public void mouseEntered(MouseEvent e){}
    @Override
    public void mouseExited(MouseEvent e){}

    //Evenements souris (motion listener)
    @Override
    public void mouseMoved(MouseEvent e) {}
    @Override
    public void mouseDragged(MouseEvent e) {
        clicSouris(e);
    }

    //Evenements clavier
    @Override
    public void keyTyped(KeyEvent e) {
        if (e.getKeyChar() == ' '){
            panel.simuler(1);
            panel.repaint();
        }
    }
    @Override
    public void keyPressed(KeyEvent e) {}
    @Override
    public void keyReleased(KeyEvent e) {}

    private class MyPanel extends JScrollPane implements ActionListener {
        
        private int grille[][];
        int tailleGrille;
        private javax.swing.Timer clock;
        
        public MyPanel(int taille) {
            this.setBackground(Color.white);
            this.setAutoscrolls(rootPaneCheckingEnabled);
            tailleGrille = taille;
            grille  = new int[taille][taille];
        }
        
        @Override
        public void actionPerformed(ActionEvent e){
            try {
            InputStream in = new FileInputStream("sounds/clocktick.wav");
            AudioStream as = new AudioStream(in);
            AudioPlayer.player.start(as);
            } catch (Exception exc){}
            
            simulerRafraichir(ihm.getSpinnerStepValue());
        }
        
        private void lancerTimer(int millisecondes){
            clock = new javax.swing.Timer(millisecondes, this);
            clock.start();
        }
        
        private void stoperTimer(){
            clock.stop();
            clock = null;
        }
        
        private void simuler(int nombrePas) {
            for (int n=0; n<nombrePas; n++) {
                //enregistrement de chaque pas en .csv
                if (ihm.isExporting()){
                    ecrireCsv(ihm.getNomExport());
                }
                //copie de la grille utilisée pour analyser la situation courante
                int copyGrille[][] = new int[tailleGrille][tailleGrille];
                for(int i=0; i<grille.length; i++)
                    for(int j=0; j<grille[i].length; j++)
                        copyGrille[i][j]=grille[i][j];

                int A = 0;
                int a = 0;
                int p = 0;

                
                switch (simMode) {
                    case 0: //Simulation de croissance de la forêt
                        for(int X=1; X<tailleGrille-1; X++){
                            for(int Y=1; Y<tailleGrille-1; Y++){
                                //Switch pour chaque case de la grille 
                                switch (copyGrille[X][Y]) {
                                    case 0:
                                        //Les cases vident passent à l'état jeune pousse, sous reserve de certaines contraintes
                                        A = 0;
                                        a = 0;
                                        for (int vX=X-1; vX<=X+1; vX++) {
                                            for (int vY=Y-1; vY<=Y+1; vY++) {
                                                if ((vX==X && vY==Y)) {continue;}
                                                if (copyGrille[vX][vY]==4) {A++;}
                                                if ((copyGrille[vX][vY]==2) || (copyGrille[vX][vY]==3)) {a++;}
                                            }
                                        }
                                        if ((A>=2) || (a>=3) || ((A==1) && (a==2))) {
                                            //System.out.println(A+", "+a);
                                            grille[X][Y] = 1;
                                        }
                                    break;

                                    case 1:
                                        //Les jeunes pousses passent à l'état arbuste, sous reserve de certaines contraintes
                                        A = 0;
                                        a = 0;
                                        for (int vX=X-1; vX<=X+1; vX++) {
                                            for (int vY=Y-1; vY<=Y+1; vY++) {
                                                if ((vX==X && vY==Y)) {continue;}
                                                if (copyGrille[vX][vY]==4) {A++;}
                                                if ((copyGrille[vX][vY]==2) || (copyGrille[vX][vY]==3)) {a++;}
                                            }
                                        }
                                        if ((A+a)<=3) {grille[X][Y] = 2;}
                                    break;

                                    case 2:
                                        //L'arbuste passe au deuxième stade
                                        grille[X][Y] = 3;
                                    break;

                                    case 3:
                                        //L'arbuste au deuxième stade passe à l'état d'arbre
                                        grille[X][Y] = 4;
                                    break;
                                }
                            }
                        }
                    break;
                        
                    case 1: //Simulation incendie
                        int f = 0;
                        for(int X=1; X<tailleGrille-1; X++){
                            for(int Y=1; Y<tailleGrille-1; Y++){
                                //Switch pour chaque case de la grille 
                                switch (copyGrille[X][Y]) {
                                    case 1: //Jeunes poussses prenant feu
                                        f = 0;
                                        //Detection d'eventuelles cases feu, voisinnage de Moore
                                        for (int vX=X-1; vX<=X+1; vX++) {
                                            for (int vY=Y-1; vY<=Y+1; vY++) {
                                                if ((vX==X && vY==Y)) {continue;}
                                                if (copyGrille[vX][vY]==5) {f++;}
                                            }
                                        }
                                        //S'il y a au moins une case feu...
                                        if (f>=1) {
                                            if (Math.random()*1 <= 0.25) {
                                                grille[X][Y] = 5;
                                            }
                                        }
                                    break;
                                        
                                    case 2: //Arbuste stade 1 prenant feu
                                        f = 0;
                                        //Detection d'eventuelles cases feu, voisinnage de Moore
                                        for (int vX=X-1; vX<=X+1; vX++) {
                                            for (int vY=Y-1; vY<=Y+1; vY++) {
                                                if ((vX==X && vY==Y)) {continue;}
                                                if (copyGrille[vX][vY]==5) {f++;}
                                            }
                                        }
                                        //S'il y a au moins une case feu...
                                        if (f>=1) {
                                            if (Math.random()*1 <= 0.5) {
                                                grille[X][Y] = 5;
                                            }
                                        }
                                    break;
                                        
                                    case 3: //Arbuste stade 2 prenant feu
                                        f = 0;
                                        //Detection d'eventuelles cases feu, voisinnage de Moore
                                        for (int vX=X-1; vX<=X+1; vX++) {
                                            for (int vY=Y-1; vY<=Y+1; vY++) {
                                                if ((vX==X && vY==Y)) {continue;}
                                                if (copyGrille[vX][vY]==5) {f++;}
                                            }
                                        }
                                        //S'il y a au moins une case feu...
                                        if (f>=1) {
                                            if (Math.random()*1 <= 0.5) {
                                                grille[X][Y] = 5;
                                            }
                                        }
                                    break;
                                        
                                    case 4: //Arbres prenant feu
                                        f = 0;
                                        //Detection d'eventuelles cases feu, voisinnage de Moore
                                        for (int vX=X-1; vX<=X+1; vX++) {
                                            for (int vY=Y-1; vY<=Y+1; vY++) {
                                                if ((vX==X && vY==Y)) {continue;}
                                                if (copyGrille[vX][vY]==5) {f++;}
                                            }
                                        }
                                        //S'il y a au moins une case feu...
                                        if (f>=1) {
                                            if (Math.random()*1 <= 0.75) {
                                                grille[X][Y] = 5;
                                            }
                                        }
                                    break;
                                        
                                    case 5:
                                        //Le feu devient cendres...
                                        grille[X][Y] = 6;
                                    break;
                                        
                                    case 6:
                                        //...Les cendres retournent à l'éternité.
                                        grille[X][Y] = 0;
                                    break;
                                }
                            }
                        }
                    break;
                        
                    case 2: //Simulation invasion d'insectes
                        int ins = 0;
                        for(int X=1; X<tailleGrille-1; X++){
                            for(int Y=1; Y<tailleGrille-1; Y++){
                                //Switch pour chaque case de la grille 
                                switch (copyGrille[X][Y]){
                                    case 1:
                                        ins = 0;
                                        //Detection d'eventuelles cases insectes, voisinnage de Von Neumann
                                        if (copyGrille[X-1][Y]==7 ||
                                                copyGrille[X+1][Y]==7 ||
                                                copyGrille[X][Y-1]==7 ||
                                                copyGrille[X][Y+1]==7) {
                                            ins++;
                                        }
                                        //S'il y a au moins une case insectes...
                                        if (ins>=1) {
                                            if (Math.random()*1 <= 0.75) {
                                                grille[X][Y] = 7;
                                            }
                                        }
                                    break;
                                        
                                    case 2:
                                        ins = 0;
                                        //Detection d'eventuelles cases insectes, voisinnage de Von Neumann
                                        if (copyGrille[X-1][Y]==7 ||
                                                copyGrille[X+1][Y]==7 ||
                                                copyGrille[X][Y-1]==7 ||
                                                copyGrille[X][Y+1]==7) {
                                            ins++;
                                        }
                                        //S'il y a au moins une case insectes...
                                        if (ins>=1) {
                                            if (Math.random()*1 <= 0.5) {
                                                grille[X][Y] = 7;
                                            }
                                        }
                                    break;
                                        
                                    case 3:
                                        ins = 0;
                                        //Detection d'eventuelles cases insectes, voisinnage de Von Neumann
                                        if (copyGrille[X-1][Y]==7 ||
                                                copyGrille[X+1][Y]==7 ||
                                                copyGrille[X][Y-1]==7 ||
                                                copyGrille[X][Y+1]==7) {
                                            ins++;
                                        }
                                        //S'il y a au moins une case insectes...
                                        if (ins>=1) {
                                            if (Math.random()*1 <= 0.5) {
                                                grille[X][Y] = 7;
                                            }
                                        }
                                    break;
                                        
                                    case 4:
                                        ins = 0;
                                        //Detection d'eventuelles cases insectes, voisinnage de Von Neumann
                                        if (copyGrille[X-1][Y]==7 ||
                                                copyGrille[X+1][Y]==7 ||
                                                copyGrille[X][Y-1]==7 ||
                                                copyGrille[X][Y+1]==7) {
                                            ins++;
                                        }
                                        //S'il y a au moins une case insectes...
                                        if (ins>=1) {
                                            if (Math.random()*1 <= 0.25) {
                                                grille[X][Y] = 7;
                                            }
                                        }
                                    break;
                                        
                                    case 7:
                                        grille[X][Y] = 0;
                                    break;
                                }
                            }
                        }
                    break;
                }
                 //Clock auto stop (le son du micro ondes)
                if (clock != null){
                    if (Arrays.deepEquals(grille, copyGrille) && (clock.isRunning())) {
                        clock.stop();
                        ihm.stopAuto();

                        try {
                            InputStream in = new FileInputStream("sounds/bell.wav");
                            AudioStream as = new AudioStream(in);
                            AudioPlayer.player.start(as);
                        } catch (Exception e){}

                    }
                }
            }
        }
        
        @Override
        public void paint(Graphics g) {
            
            int W = getWidth();
            int H = getHeight();
            
            //Remplissage du pannel en gris
            g.setColor(Color.LIGHT_GRAY);
            g.fillRect(0, 0, W, H);
            
            //Dessin du "plateau" blanc
            g.setColor(Color.WHITE);
            g.fillRect(5, 5, (tailleGrille-2)*5, (tailleGrille-2)*5);
            
            //Dessin de la grille
            g.setColor(Color.LIGHT_GRAY);
            for(int i=5; i<(tailleGrille-1)*5; i+=5) {
                g.drawLine(i, 5, i, (tailleGrille-1)*5);
            }
            for(int j=5; j<(tailleGrille-1)*5; j+=5) {
                g.drawLine(5, j, (tailleGrille-1)*5, j);
            }
            
            //Dessin des cases (forêt, feu, insectes)
            for(int X=0; X<tailleGrille; X++){
                for(int Y=0; Y<tailleGrille; Y++){
                    //1 : type jeune pousse
                    if (grille[X][Y] == 1) {
                        g.setColor(new Color(161, 214, 92));
                        g.fillRect(X*5+1, Y*5+1, 4, 4);
                    }
                    //2 & 3 : type arbuste phase 1 & 2
                    if ((grille[X][Y] == 2) || (grille[X][Y] == 3)) {
                        g.setColor(new Color(19, 173, 47));
                        g.fillRect(X*5+1, Y*5+1, 4, 4);
                    }
                    //4 : arbuste
                    if (grille[X][Y] == 4) {
                        g.setColor(new Color(18, 97, 8));
                        g.fillRect(X*5+1, Y*5+1, 4, 4);
                    }
                    //5 : feu
                    if (grille[X][Y] == 5) {
                        g.setColor(new Color(255, 0, 0));
                        g.fillRect(X*5+1, Y*5+1, 4, 4);
                    }
                    //6 : cendres
                    if (grille[X][Y] == 6) {
                        g.setColor(new Color(192, 192, 192));
                        g.fillRect(X*5+1, Y*5+1, 4, 4);
                    }
                    //7 : insectes
                    if (grille[X][Y] == 7) {
                        g.setColor(new Color(192, 32, 255));
                        g.fillRect(X*5+1, Y*5+1, 4, 4);
                    }
                }
            }
        }
    }
}
